home *** CD-ROM | disk | FTP | other *** search
/ NeXT Education Software Sampler 1992 Fall / NeXT Education Software Sampler 1992 Fall.iso / Mathematics / Notebooks / SigProc2.0 / Packages / SignalProcessing / Support / LatticeTheory.m < prev    next >
Encoding:
Text File  |  1992-08-18  |  47.3 KB  |  1,410 lines

  1. (*  :Title:    Routines to implement concepts from Lattice Theory  *)
  2.  
  3. (*  :Authors:    Brian Evans, James McClellan  *)
  4.  
  5. (*
  6.     :Summary:   Provide some of the functions which are common in lattice
  7.         theory (such as computing distinct coset vectors) and the
  8.         geometry of numbers (such as the Bezout identity and
  9.         and generalized Euclid factors) that are not implemented
  10.         in Mathematica.  These routines form the basis for
  11.         multirate signal processing.
  12.  *)
  13.  
  14. (*  :Context:    SignalProcessing`Support`LatticeTheory`  *)
  15.  
  16. (*  :PackageVersion:  2.7    *)
  17.  
  18. (*
  19.     :Copyright:    Copyright 1989-1992 by Brian L. Evans
  20.         Georgia Tech Research Corporation
  21.  
  22.     Permission to use, copy, modify, and distribute this software
  23.     and its documentation for any purpose and without fee is
  24.     hereby granted, provided that the above copyright notice
  25.     appear in all copies and that both that copyright notice and
  26.     this permission notice appear in supporting documentation,
  27.     and that the name of the Georgia Tech Research Corporation,
  28.     Georgia Tech, or Georgia Institute of Technology not be used
  29.     in advertising or publicity pertaining to distribution of the
  30.     software without specific, written prior permission.  Georgia
  31.     Tech makes no representations about the suitability of this
  32.     software for any purpose.  It is provided "as is" without
  33.     express or implied warranty.
  34.  *)
  35.  
  36. (*  :History:    multirate signal processing  *)
  37.  
  38. (*  :Keywords:    downsampling, sampling matrix, upsampling *)
  39.  
  40. (*
  41.     :Source:         R. Bamberger, {The Directional Filter Bank:  A Multirate
  42.         Filter Bank for the Directional Decompostion of Images},
  43.         Georgia Tech Ph. D. Thesis, 1990.
  44.  
  45.              T. Kailath, {Linear Systems}, Prentice Hall, Englewood
  46.         Cliffs, NJ, 1980.
  47.  
  48.              A. Kaufmann and A. Henry-Labordere, {Integer and
  49.         Mixed Progamming: Theory and Applications}, Academic Press,
  50.         New York, 1977.
  51.  
  52.              J. Kovacevic and M. Veterli, "Perfect Reconstruction
  53.         Filter Banks with Rational Sampling Rates in One- and
  54.         Two-dimensions," in {Proc. SPIE Conference on Visual
  55.         Communications and Image Processing} (Philadelphia, PA),
  56.         pp. 1258-1268, November 1989.
  57.  
  58.              C. MacDuffee, {The Theory of Matrices}, Springer-Verlag,
  59.         Berlin, Germany, 1933.
  60.  *)
  61.  
  62. (*  :Warning:    *)
  63.  
  64. (*  :Mathematica Version:  1.2 or 2.0  *)
  65.  
  66. (*  :Limitation:  *)
  67.  
  68. (*
  69.     :Discussion:     The built-in integer division operation Quotient does
  70.         not work properly under Mathematica 1.2.  Therefore, we have
  71.         added a local routine called intdivide.
  72.  
  73.              These routines underlie the "algebra" for multidimensional
  74.         multirate systems.  They are a combination of concepts from
  75.         matrix theory and the geometry of numbers.
  76.  
  77.              The lone Mathematica lattice operation, LatticeReduce,
  78.         is the Lenstra-Lenstra-Lovasz lattice reduction algorithm
  79.         which makes the basis vectors of a lattice more orthogonal.
  80.         Another way to perform this operation is to compute the
  81.         Smith Form decomposition of the integer matrix and discard
  82.         the right regular unimodular matrix factor
  83.  *)
  84.  
  85. (*
  86.     :Functions:    BezoutNumbers
  87.         ColumnHermiteForm
  88.         CommutableResamplingMatricesQ
  89.         DiagonalMatrixQ
  90.         DistinctCosetVectors
  91.         GCLD
  92.         GCRD
  93.         IncList
  94.         ImpulseTrain
  95.         IntegerVectorQ
  96.         LCLM
  97.         LCRM
  98.         NormalizeSamplingMatrix
  99.         RegUnimodularResMatrixQ
  100.         RelativelyPrime 
  101.         ReorderResampling
  102.         ResamplingMatrix
  103.         ResamplingMatrixMod
  104.         RowHermiteForm
  105.         SmithFormSameU
  106.         SmithFormSameV
  107.         SmithNormalForm
  108.         SmithOrderedForm
  109.         SmithReducedForm
  110.         UpsampledSystem
  111.  *)
  112.  
  113.  
  114. If [ TrueQ[ $VersionNumber >= 2.0 ],
  115.      Off[ General::spell ];
  116.      Off[ General::spell1 ] ]
  117.  
  118.  
  119. (*  Only for the release of LatticeTheory as an independent package  *)
  120.  
  121. (*  
  122. If [ SameQ[ Head[Dialogue::usage], MessageName ],
  123.      Dialogue::usage =
  124.     "Dialogue is an option for certain routines to justify their answers. \
  125.     Possible settings are False, True, or All, for no, partial, or \
  126.     full justification, respectively." ]
  127.  *)
  128.  
  129.  
  130. (*  B E G I N     P A C K A G E  *)
  131.  
  132.  
  133. BeginPackage[ "SignalProcessing`Support`LatticeTheory`",
  134.           "SignalProcessing`Support`SupCode`" ]
  135.  
  136.  
  137. (*  U S A G E     I N F O R M A T I O N  *)
  138.  
  139. BezoutNumbers::usage =
  140.     "BezoutNumbers[a, b] gives the Bezout numbers of integers a and b. \
  141.     That is, it returns {lambda, mu} so that lambda a + mu b == gcd(a,b). \
  142.     In this case, BezoutNumbers simply calls ExtendedGCD. \
  143.     For the collection of integers a1, a2, ..., an, \
  144.     BezoutNumbers[a1, a2, ..., an] returns a set (list) of of numbers \
  145.     k1, k2, ..., kn such that k1 a1 + k2 a2 + ... + kn an = d, \
  146.     where d is the greatest common divisor of a1, a2, ..., an. \
  147.     See also ExtendedGCD."
  148.  
  149. ColumnHermiteForm::usage =
  150.     "ColumnHermiteForm[F] returns {X, H} where X is the m x m regular \
  151.     unimodular matrix that puts the m x n integer matrix F into upper \
  152.     triangular (column Hermite) form H (which is m x n). \
  153.     That is, X . F = H."
  154.  
  155. CommutableResamplingMatricesQ::usage =
  156.     "CommutableResamplingMatricesQ[downsamplingmat, upsamplingmat] \
  157.     returns True if the two resampling operations are commutable."
  158.  
  159. DiagonalMatrixQ::usage =
  160.     "DiagonalMatrixQ[matrix] returns True if the matrix is diagonal, \
  161.     and gives False otherwise."
  162.  
  163. DistinctCosetVectors::usage =
  164.     "DistinctCosetVectors[resmat] returns a sorted list of all of the \
  165.     distinct coset vectors associated with the resampling matrix resmat. \
  166.     DistinctCosetVectors[resmat, U^-1, Lambda, V^-1] finds all of the \
  167.     distinct coset vectors of resmat from its Smith Form  U Lambda V. \
  168.     This is carried by finding the distinct coset vectors of the diagonal \
  169.     matrix Lambda, mapping them by U^-1, and then taking each vector \
  170.     modulo the resampling matrix resmat. \
  171.     The returned coset vectors are not sorted." 
  172.  
  173. EuclidFactors::usage =
  174.     "EuclidFactors[k, p, q] gives the factors a and b such that \
  175.     p a + q b == k for relatively prime p and q. \
  176.     When p and q are integers, this routine searches over a set of \
  177.     min(|p|, |q|) integers. \
  178.     When p and q are integer matrices, this routine searches over a \
  179.     set of min(|det(p)|, |det(q)|) integer vectors and the vector a \
  180.     or b corresponding to the integer matrix with the smallest \
  181.     determinant will always be a distinct coset of that matrix. \
  182.     See also RelativelyPrime."
  183.  
  184. GCLD::usage =
  185.     "GCLD[a, b] finds a greatest common left divisor of resampling \
  186.     matrices a and b which is unique to within the product of \
  187.     a regular unimodular matrix on the right. \
  188.     It forms F = {{a, b}, {0, 0}} and solves the equation F . X = H \
  189.     where X is a square regular unimodular matrix and H is the row \
  190.     Hermite (lower-triangular) form of the rectangular matrix F. \
  191.     Of the four submatrices of H, only H11 is non-zero. \
  192.     The GCLD of a and b is H11. \
  193.     This routine supports a Dialogue option. \
  194.     See also LCLM, RowHermiteForm, and ResamplingMatrix."
  195.  
  196. GCRD::usage =
  197.     "GCRD[a, b] returns a greatest common right divisor of resampling \
  198.     matrices a and b which is unique to within the product of \
  199.     a regular unimodular matrix on the left. \
  200.     It forms F = {{a, 0}, {b, 0}} and solves the equation X . F = H \
  201.     where X is a square regular unimodular matrix and \
  202.     H is the column Hermite (upper-triangular) form of \
  203.     the rectangular matrix F. \
  204.     Of the four submatrices of H, only H11 is non-zero. \
  205.     H11 is the GCRD. \
  206.     This routine supports a Dialogue option. \
  207.     See also LCRM, ColumnHermiteForm, and ResamplingMatrix."
  208.  
  209. IncList::usage =
  210.     "IncList[list, start, end] will increment the first element of list. \
  211.     If this becomes greater than the first element of end, \
  212.     then the next element of list will be incremented, and so forth. \
  213.     For example, IncList[{9,0,0}, {0,0,0}, {10,10,10}] would return \
  214.     {0,1,0} and IncList[{9,9,9}, {0,0,0}, {10,10,10}] would return \
  215.     {10,10,10}. \
  216.     This is useful when enumerating values over an \
  217.     n-dimensional rectangular prism."
  218.  
  219. ImpulseTrain::usage =
  220.     "ImpulseTrain[D, n] represents an infinite collection of \
  221.     Kronecker impulse functions. \
  222.     In one-dimension, D and n are integers. \
  223.     The multidimensional separable form is when both D and n are \
  224.     integer vectors of the same length. \
  225.     In the non-separable case, D is a resampling matrix and \
  226.     n is an integer vector. \
  227.     This function gives 1 whenever D^-1 n is an integer (vector), and \
  228.     gives 0 otherwise. \
  229.     See also ResamplingMatrix and Impulse."
  230.  
  231. IntegerVectorQ::usage =
  232.     "IntegerVectorQ[arg] returns True if arg is a vector whose \
  233.     elements are integers."
  234.  
  235. LCLM::usage =
  236.     "LCLM[a, b] returns a least common left multiple of resampling \
  237.     matrices a and b which is unique to within the product of \
  238.     a regular unimodular matrix on the left. \
  239.     It forms F = {{a, 0}, {b, 0}} and solves the equation X . F = H \
  240.     where X is a regular unimodular matrix and H is the column Hermite \
  241.     (upper-triangular) form of the rectangular matrix F. \
  242.     Of the four submatrices of H, only H11 is non-zero. \
  243.     The LCLM of a and b is (X21 . a) = -(X22 . b). \
  244.     This routine supports a Dialogue option. \
  245.     See also GCRD, ColumnHermiteForm, and ResamplingMatrix."
  246.  
  247. LCRM::usage =
  248.     "LCRM[a, b] finds a least common right multiple of resampling \
  249.     matrices a and b which is unique to within the product of \
  250.     a regular unimodular matrix on the right. \
  251.     It forms F = {{a, b}, {0, 0}} and solves the equation F . X = H \
  252.     where X is a regular unimodular matrix and H is the row Hermite \
  253.     (lower-triangular) form of the rectangular matrix F. \
  254.     Of the four submatrices of H, only H11 is non-zero. \
  255.     The LCRM of a and b is (a . X12) = -(b . X22). \
  256.     This routine supports a Dialogue option. \
  257.     See also GCLD, RowHermiteForm, and ResamplingMatrix."
  258.  
  259. NormalizeSamplingMatrix::usage =
  260.     "NormalizeSamplingMatrix[sampmat] will decompose the sampling matrix \
  261.     sampmat into a diagonal matrix D and a normalized sampling matrix V. \
  262.     The results are returned as { D, V }."
  263.  
  264. ReorderResampling::usage =
  265.     "ReorderResampling[resmat, vars, allvars] rewrites \
  266.     the resampling matrix resmat and the variables vars associated \
  267.     with it so that the resampling is carried out by the order \
  268.     of variables specified by allvars. \
  269.     Therefore, vars must be a subset of allvars."
  270.  
  271. RegUnimodularResMatrixQ::usage =
  272.     "RegUnimodularResMatrixQ[mat] gives Ture if mat is a resampling \
  273.     matrix with a determinant of +1 or -1, and gives False otherwise. \
  274.     See also ResamplingMatrix."
  275.  
  276. RelativelyPrime::usage =
  277.     "RelativelyPrime[a, b] returns True if a and b are relatively prime, \
  278.     and gives False otherwise. \
  279.     The data types of a and b must be the same. \
  280.     Possible data types are integers, polynomials, and \
  281.     square integer matrices (a.k.a. resampling matrices). \
  282.     For non-diagonal resampling matrices, a third argument of \
  283.     Right or Left is necessary to direct the matrix factorization \
  284.     to occur on the right (using GCLD) or left (using GCRD). \
  285.     See also GCD, PolynomialGCD, and ResamplingMatrix."
  286.  
  287. ResamplingMatrix::usage =
  288.     "ResamplingMatrix[arg] returns True if arg is a square matrix \
  289.     whose elements are integers and whose determinant is non-zero. \
  290.     It returns False if arg is not a square matrix. \
  291.     It also returns False if arg is a square matrix with one element \
  292.     being a number that is not an integer. \
  293.     Otherwise, it returns the conditions for which the square matrix \
  294.     would be a resampling matrix."
  295.  
  296. ResamplingMatrixMod::usage =
  297.     "ResamplingMatrixMod[n, N] implements the integer vector n \
  298.     modulo a sampling matrix N which is defined as \
  299.     n - N . Floor[ N^-1 . n ]. \
  300.     The operation will be carried out if and only if n is an integer \
  301.     vector, N is (or could be) a sampling matrix, and \
  302.     N . n is a valid operation."
  303.  
  304. RowHermiteForm::usage =
  305.     "RowHermiteForm[F] returns {X, H} where X is the n x n regular \
  306.     unimodular matrix that puts the m x n integer matrix F into \
  307.     lower triangular (row Hermite) form H (which is m x n). \
  308.     That is, F . X = H."
  309.  
  310. SmithFormSameU::usage =
  311.     "SmithFormSameU[smithfun, m1, m2] computes the Smith Form of the \
  312.     integer matrix m1 so that m1 = u1 d1 v1. \
  313.     The routine then uses u2 = u1 to decompose m2. \
  314.     The first argument specifies which decomposition routine should \
  315.     be used to decompose m1 (e.g., SmithNormalForm). \
  316.     The routine returns {cond, {u1^-1, d1, v1^-1}, {u2^-1, d2, v2^-1}}, \
  317.     where cond is True of False depending on whether or not matrices \
  318.     m1 and m2 can be decomposed using the same U."
  319.  
  320. SmithFormSameV::usage =
  321.     "SmithFormSameV[smithfun, m1, m2] computes the Smith Form of the \
  322.     integer matrix m1 so that m1 = u1 d1 v1. \
  323.     The routine then uses v2 = v1 to decompose m2. \
  324.     The first argument specifies which decomposition routine should \
  325.     be used to decompose m1 (e.g., SmithNormalForm). \
  326.     The routine returns {cond, {u1^-1, d1, v1^-1}, {u2^-1, d2, v2^-1}}, \
  327.     where cond is True of False depending on whether or not matrices \
  328.     m1 and m2 can be decomposed using the same V."
  329.  
  330. SmithNormalForm::usage =
  331.     "SmithNormalForm[A] returns a list of three matrices \
  332.     U^-1, D, and V^-1 such that the m x n resampling matrix A \
  333.     equals U D V where U (m x m) and V (n x n) are integer matrices \
  334.     with determinants of +1 or -1 and D is a diagonal matrix \
  335.     of integer components such that |det(D)| = |det(A)|. \
  336.     The algorithm follows Kaufmann's {Integer and Mixed Programming}. \
  337.     Note that this factorization is not unique. \
  338.     SmithNormalForm supports a Dialogue option. \
  339.     If Dialogue is True or All, SmithNormalForm will show immediate steps. \
  340.     See also SmithOrderedForm and SmithReducedForm."
  341.  
  342. SmithOrderedForm::usage =
  343.     "SmithOrderedForm[A] returns a list of three matrices \
  344.     U, D, and V such that the m x n resampling matrix A \
  345.     equals U D V where U (m x m) and V (n x n) are integer matrices \
  346.     with a determinant of 1 and D is a diagonal matrix \
  347.     of integer components such that det(D) = det(A). \
  348.     The components along the diagonal of D are sorted by absolute value. \
  349.         The diagonal elements are positive except for possibly the \
  350.     last one whose sign will be the sign of det(A). \
  351.     Note that this factorization is unique if none of the diagonal \
  352.     elements are equal in absolute value. \
  353.     Also note that the starting point is the result of the Smith \
  354.     Normal Form decomposition. \
  355.     Like SmithNormalForm, SmithOrderedForm supports a Dialogue option. \
  356.     If True or All, SmithNormalForm will show immediate steps. \
  357.     See also SmithNormalForm and SmithReducedForm."
  358.  
  359. SmithReducedForm::usage =
  360.     "SmithReducedForm[A] returns a list of three matrices \
  361.     U, D, and V such that the m x n resampling matrix A \
  362.     equals U D V where U (m x m) and V (n x n) are integer matrices \
  363.     with a determinant of +1 or -1 and D is a diagonal matrix \
  364.     of positive integers such that |det(D)| = |det(A)|. \
  365.     Each component down the diagonal is a factor of the next one. \
  366.     As a result, the diagonal elements are sorted down the diagonal. \
  367.     The factorization is not unique. \
  368.     Note that the starting point for this algorithm is the Smith \
  369.     Ordered Form. \
  370.     Like SmithOrderedForm, SmithReducedForm supports a Dialogue option. \
  371.     If True or All, SmithNormalForm will show immediate steps. \
  372.     See also SmithNormalForm and SmithOrderedForm."
  373.  
  374. UpsampledSystem::usage =
  375.     "UpsampledSystem[expr, vars] returns a resampling matrix and \
  376.     a set of linearly coupled variables taken from the master list vars. \
  377.     For example, UpsampledSystem[Cos[w1 + w2] Sin[w1 - w2], {w1, w2} ] \
  378.     will return { {{1,1},{1,-1}}, {w1,w2} }. \
  379.     In this case, the dot product of the resampling matrix and the \
  380.     variable list give {w1 + w2, w1 - w2}. \
  381.     A third optional argument would be a function to extract coefficients \
  382.     that takes an expression, a list of variables, and a value to return \
  383.     on failure."
  384.  
  385. (*  E N D     U S A G E     I N F O R M A T I O N  *)
  386.  
  387.  
  388. Begin[ "`Private`" ]
  389.  
  390.  
  391. (*  M E S S A G E S  *)
  392.  
  393. BezoutNumbers::error =
  394.     "BezoutNumbers[``, ``] with gcd = `` could not be found."
  395.  
  396. CommutableResamplingMatricesQ::invalid =
  397.     "At least one of the arguments is not a resampling matrix."
  398.  
  399. DistinctCosetVectors::notresmat =
  400.     "The lone argument is not a resampling matrix: ``"
  401. DistinctCosetVectors::zerodet = "Resampling matrix has zero determinant."
  402.  
  403. EuclidFactors::notrelprime = "`` and `` are not relatively prime ``."
  404.  
  405. LCLM::badgcd = "The (``,``) position of F is not the gcd of column i"
  406.  
  407. SmithNormalForm::invalid =
  408.     "The determinants of U^-1 and V^-1 should both be one."
  409.  
  410.  
  411. (*  S U P P O R T I N G     R O U T I N E S  *)
  412.  
  413.  
  414. (* identitymatrix *)
  415. identitymatrix[{k_Integer, k_Integer}] := IdentityMatrix[k]
  416. identitymatrix[{m_Integer, n_Integer}] :=
  417.     Block [    {i, imat, zerovector},
  418.         zerovector = Table[0, {n}];
  419.         imat = Table[ zerovector, {m} ];
  420.         For [ i = 1, i <= m, i++,
  421.               imat[[i,i]] = 1 ];
  422.         imat ] /;
  423.     ( m != n )
  424.  
  425. (* intdivide --  kludge for Quotient primitive which has a problem  *)
  426. (*         under Mma 1.2 when the second argument is negative *)
  427. If [ TrueQ[ $VersionNumber >= 2.0 ],
  428.      intdivide[n_, m_] := Quotient[n, m],
  429.      intdivide[n_, m_] := Sign[m] Quotient[n, m Sign[m]] ]
  430.  
  431. (* mergerows *)
  432. mergerows[ {x__} ] := Join[x]
  433.  
  434. (* matrix4to1 --  combine four component matrices into 1 *)
  435. matrix4to1[a11_, a12_, a21_, a22_] :=
  436.     Map[ mergerows, Transpose[{Join[a11, a21], Join[a12, a22]}] ]
  437.  
  438. (* matrix1to4 --  split one matrix into its four m x n component matrices *)
  439. matrix1to4[mat_] := matrix1to4[ mat, Dimensions[mat] ]
  440. matrix1to4[mat_, {m_, n_}] :=
  441.     Block [    {a11, a12, a21, a22, i, j},
  442.         a11 = Table[ mat[[i,j]], {i, 1, m}, {j, 1, n} ];
  443.         a12 = Table[ mat[[i,j]], {i, 1, m}, {j, n+1, 2 n} ];
  444.         a21 = Table[ mat[[i,j]], {i, m+1, 2 m}, {j, 1, n} ];
  445.         a22 = Table[ mat[[i,j]], {i, m+1, 2 m}, {j, n+1, 2 n} ];
  446.         {{a11, a12}, {a21, a22}} ]
  447.  
  448. (* myabs *)
  449. myabs[x_List] := Min[Abs[Select[x, nonzero]]]
  450.  
  451. (*  mymod  *)
  452. SetAttributes[mymod, {Listable}]
  453. mymod[x_] := Mod[x, 1]
  454.  
  455. (* nonzero *)
  456. nonzero[x_] := ( x != 0 )
  457.  
  458. (*  onefun  *)
  459. onefun[x__] := 1
  460.  
  461. (*  toList  *)
  462. toList[] := {}
  463. toList[arg_List] := arg
  464. toList[arg_] := List[arg] /; ! SameQ[Head[arg], List]
  465. toList[arg1_, args__] := List[arg1, args]
  466.  
  467. (*  vectorgcd   *)
  468. vectorgcd[ x_ ] := Apply[GCD, x]
  469.  
  470.  
  471. (* FindMinMatrixElement *)
  472. FindMinMatrixElement[mat_List] :=
  473.     Block [ {item},
  474.         item = myabs[ Flatten[mat] ];
  475.         Join[ Position[ mat, item ], Position[ mat, -item ] ] ]
  476.  
  477. (* LCLMandGCRDintroduction *)
  478. LCLMandGCRDintroduction[] :=
  479.     Block [ {matrixborder},
  480.         matrixborder = MatrixForm[ {"|", "|"} ];
  481.         Print[ "  This routine builds the matrix X such that" ];
  482.         Print[ "     ", matrixborder,
  483.                MatrixForm[{{"X11", "X12"}, {"X21", "X22"}}],
  484.                matrixborder, "  ", matrixborder,
  485.                MatrixForm[{{"A", 0}, {"B", 0}}],
  486.                matrixborder, Superscript[" = "], matrixborder,
  487.                MatrixForm[{{"H", 0}, {0, 0}}], matrixborder ];
  488.         Print[ "where A and B are the two resampling matrices." ];
  489.         Print[ "X is composed of a product of unimodular" ];
  490.         Print[ "matrices.  The least common left multiple is:" ];
  491.         Print[ "M = X21 A = -X22 B and the greatest common" ];
  492.         Print[ "right divisor of A and B is H." ] ]
  493.  
  494. (* LCRMandGCLDintroduction *)
  495. LCRMandGCLDintroduction[] :=
  496.     Block [ {matrixborder},
  497.         matrixborder = MatrixForm[ {"|", "|"} ];
  498.         Print[ "  This routine builds the matrix X such that" ];
  499.         Print[ "     ", matrixborder,
  500.                MatrixForm[{{"A", "B"}, {0, 0}}],
  501.                matrixborder, "  ", matrixborder,
  502.                MatrixForm[{{"X11", "X12"}, {"X21", "X22"}}],
  503.                matrixborder, Superscript[" = "], matrixborder,
  504.                MatrixForm[{{"H", 0}, {0, 0}}], matrixborder ];
  505.         Print[ "where A and B are the two resampling matrices." ];
  506.         Print[ "X is composed of a product of unimodular" ];
  507.         Print[ "matrices.  The least common right multiple is:" ];
  508.         Print[ "M = A X12 = -B X22 and the greatest common" ];
  509.         Print[ "left divisor of A and B is H.  By transposing" ];
  510.         Print[ "both sides of the equation, the solution steps" ];
  511.         Print[ "are the same as carried out by the LCLM and" ];
  512.         Print[ "GCRD routines.  This how the LCRM and GCLD" ];
  513.         Print[ "routines work." ] ]
  514.  
  515. (* TransitionMatrix *)
  516. TransitionMatrix[r1_, r2_, dim_] :=
  517.         TransitionMatrix[r1, r2, dim, IdentityMatrix[dim]]
  518. TransitionMatrix[r1_, r2_, dim_, mat_] :=
  519.     Block [    {resmat, temp},
  520.         resmat = mat;
  521.         temp = resmat[[r1]];
  522.         resmat[[r1]] = resmat[[r2]];
  523.         resmat[[r2]] = temp;
  524.         resmat ]
  525.  
  526.  
  527. (* SmithFormA --  returns non-zero minimum value, left transition *)
  528. (*   matrix, and right transition matrix of the current matrix.   *)
  529. SmithFormA[ curmat_, dim_, m_, n_, identm_, identn_ ] :=
  530.     Block [    {col, i, minvalue, row, workmat},
  531.         workmat = Table[ Take[ curmat[[i]], {1 + dim, n} ],
  532.                  {i, 1 + dim, m} ];
  533.         {row, col} = First[FindMinMatrixElement[workmat]];
  534.         { curmat[[row + dim]][[col + dim]],
  535.           TransitionMatrix[dim + 1, row + dim, m, identm],
  536.           TransitionMatrix[dim + 1, col + dim, n, identn] } ]
  537.  
  538. (* SmithFormB --  returns the left and right subtraction matrices *)
  539. SmithFormB[ curmat_, dim_, minvalue_, m_, n_, identm_, identn_ ] :=
  540.     Block [    {a, i, j, submatleft, submatright},
  541.  
  542.         submatleft = identm;
  543.         For [ i = dim + 2, i <= m, i++,
  544.               a = curmat[[i, 1 + dim]];
  545.               If [ ! SameQ[a, 0],
  546.                    submatleft[[i, 1+dim]] = intdivide[-a, minvalue]]];
  547.  
  548.         submatright = identn;
  549.         For [ j = dim + 2, j <= n, j++,
  550.               a = curmat[[1 + dim, j]];
  551.               If [ ! SameQ[a, 0],
  552.                 submatright[[1+dim, j]] = intdivide[-a, minvalue]]];
  553.  
  554.         { submatleft, submatright } ]
  555.  
  556. (* SmithFormEndTest --  end if the elements in the first column  *)
  557. (*   and first row of the working matrix except (1,1) are zero.  *)
  558. SmithFormEndTest[curmat_, dim_, m_, n_] :=
  559.     Block [ {firstrowcollist},
  560.  
  561.         firstrowcollist = Join[ Take[ curmat [[dim+1]], {dim+2, m} ],
  562.                     Take[ Transpose[curmat] [[dim + 1]],
  563.                           {dim + 2, n} ] ];
  564.  
  565.         SameQ[First[firstrowcollist], 0] &&
  566.           Apply[Equal, firstrowcollist] ]
  567.  
  568. (* SmithFormTrace *)
  569. SmithFormTrace[ dialogue_, str_, mat_ ] :=
  570.     If [ dialogue || SameQ[dialogue, All],
  571.          Print[ ]; Print[ str ]; Print[ MatrixForm[mat] ] ]
  572.  
  573. SmithFormTrace[ dialogue_, str_ ] :=
  574.     If [ dialogue || SameQ[dialogue, All], Print[ ]; Print[ str ] ]
  575.  
  576. SmithFormTrace2[ dialogue_, str_, mat_ ] :=
  577.     If [ SameQ[dialogue, All],
  578.          Print[ ];
  579.          Print[ "  ", str, " subtraction matrix:" ];
  580.          Print[ "  ", MatrixForm[mat] ] ]
  581.  
  582.  
  583. (*  E X P O R T E D     R O U T I N E S  *)
  584.  
  585. (*  BezoutNumbers --  only examine one period of possible values  *)
  586. (*    to find more than two Bezout numbers, the key to do it by   *)
  587. (*    induction:  generate the Bezout numbers for the first two;  *)
  588. (*    then do it for the GCD of the first two no. and the third;  *)
  589. (*    adjust the first two coefficients accordingly...            *)
  590. BezoutNumbers[a_Integer, b_Integer] := ExtendedGCD[a, b] [[2]]
  591.  
  592. BezoutNumbers[a_Integer, b_Integer, rest__] :=
  593.     Block [ {bezlist, intlist, lastgcd, j, n, newgcd, term},
  594.         intlist = {a, b, rest};
  595.         n = Length[intlist];
  596.  
  597.         {lastgcd, bezlist} = ExtendedGCD[a, b];
  598.         For [ j = 3, j <= n, j++,
  599.               term = intlist[[j]];
  600.               newgcd = GCD[lastgcd, term];
  601.               {p, q} = bezoutwithgcd[lastgcd, term, newgcd];
  602.               bezlist *= p;
  603.               AppendTo[bezlist, q];
  604.               lastgcd = newgcd ];
  605.         bezlist ] /;
  606.     IntegerVectorQ[ {a, b, rest} ]
  607.  
  608. bezoutwithgcd[0, b_Integer, gcd_Integer] := {0, Sign[b]}
  609. bezoutwithgcd[a_Integer, 0, gcd_Integer] := {Sign[a], 0}
  610.  
  611. bezoutwithgcd[a_Integer, b_Integer, gcd_Integer] :=
  612.     Block [ {anorm, bnorm, lambda, mu, muvalue, mumax},
  613.  
  614.         anorm = a / gcd;
  615.         bnorm = b / gcd;
  616.         mumax = Abs[anorm];
  617.         For [ mu = 0, mu < mumax, mu++,
  618.               lambda = ( 1 - mu bnorm ) / anorm;
  619.               If [ IntegerQ[lambda], muvalue = mu; Break[] ] ];
  620.  
  621.         If [ ! IntegerQ[lambda],
  622.              Message[ BezoutNumbers::error, a, b, gcd ] ];
  623.  
  624.         { lambda, muvalue } ] /;
  625.     ( a <= b )
  626.  
  627. bezoutwithgcd[a_Integer, b_Integer, gcd_Integer] :=
  628.     Block [ {anorm, bnorm, lambda, lambdavalue, mu},
  629.  
  630.         anorm = a / gcd;
  631.         bnorm = b / gcd;
  632.         lambdamax = Abs[bnorm];
  633.         For [ lambda = 0, lambda < lambdamax, lambda++,
  634.               mu = ( 1 - lambda anorm ) / bnorm;
  635.               If [ IntegerQ[mu], lambdavalue = lambda; Break[] ] ];
  636.  
  637.         If [ ! IntegerQ[mu],
  638.              Message[ BezoutNumbers::error, a, b, gcd ] ];
  639.  
  640.         { lambdavalue, mu } ] /;
  641.     ( a > b )
  642.  
  643. (* ColumnHermiteForm *)
  644. Options[ColumnHermiteForm] := { Dialogue -> False }
  645.  
  646. HermiteEndTest[{}] := True
  647. HermiteEndTest[ithcol_] := SameQ[First[ithcol], 0] && Apply[Equal, ithcol]
  648.  
  649. ColumnHermiteForm[ mat_, op___ ] :=
  650.     Block [    {allflag, col, dialflag, dialogue, h, hji, htrans, i,
  651.          identm, ithcol, j, m, n, oplist, pivot, r, temp, u, x},
  652.  
  653.         oplist = toList[op] ~Join~ Options[ColumnHermiteForm];
  654.         dialogue = Replace[Dialogue, oplist];
  655.         allflag = SameQ[dialogue, All];
  656.         dialflag = dialogue || allflag;
  657.  
  658.         {m, n} = Dimensions[mat];
  659.         r = Min[m, n];
  660.         x = identm = IdentityMatrix[m];        (* unimodular *)
  661.         h = mat;                (* Hermite matrix *)
  662.  
  663.         If [ dialflag,
  664.              Print[ "Putting the ", m, " x ", n, " matrix below into" ];
  665.              Print[ "upper triangular (Hermite) form:" ];
  666.              Print[ MatrixForm[h] ] ];
  667.  
  668.         For [ i = 1, i <= r, i++,
  669.  
  670.               If [ allflag, Print[ "Iteration #", i ] ];
  671.  
  672.               htrans = Transpose[h];
  673.               ithcol = Take[ htrans[[i]], {i+1, m} ];
  674.               While [ ! HermiteEndTest[ithcol],
  675.  
  676.                   (*   Move smallest element in column i *)
  677.                   (* that are below (i,i) to (i,i).      *)
  678.                   PrependTo[ ithcol, h[[i,i]] ];
  679.                   {col} = First[ FindMinMatrixElement[ ithcol ] ];
  680.                   col += i - 1;
  681.                   If [ i != col,
  682.                        temp = h[[i]];
  683.                        h[[i]] = h[[col]];
  684.                        h[[col]] = temp;
  685.                        temp = x[[i]];
  686.                        x[[i]] = x[[col]];
  687.                        x[[col]] = temp ];
  688.                   If [ allflag,
  689.                    Print[ "Hermite Form:" ];
  690.                    Print[ MatrixForm[h] ] ];
  691.  
  692.                   (* reduce elements in column i below (i,i) *)
  693.                   pivot = h[[i,i]];
  694.                   u = identm;
  695.                   Clear[j];
  696.                   For [ j = i+1, j <= m, j++,
  697.                     u[[j,i]] = intdivide[-h[[j,i]], pivot] ];
  698.                   h = u . h;
  699.                   x = u . x;
  700.  
  701.                   If [ allflag,
  702.                    Print[ "Hermite Form:" ];
  703.                    Print[ MatrixForm[h] ] ];
  704.  
  705.                       ithcol = Take[Transpose[h][[i]], {i+1, m}] ] ];
  706.  
  707.         {x, h} ] /;
  708.     MatrixQ[mat] && IntegerVectorQ[ Flatten[mat] ]
  709.  
  710. (*  CommutableResamplingMatricesQ, adapted from [Kovacevic & Vetterli, p. 4] *)
  711. (*  A cascade of a downsampling by D1 and upsampling by D2 is commutable if  *)
  712. (*  1. The matrix product is commutable, i.e.   D1 . D2 == D2 . D1         *)
  713. (*  2. The sets A and B are equivalent where                     *)
  714. (*                                         *)
  715. (*                                      t -1                     *)
  716. (*                  A = { exp(-2 pi j (D )   k), k in LatD1 }             *)
  717. (*                                      1                     *)
  718. (*                                         *)
  719. (*                                     t   t -1                     *)
  720. (*                  B = { exp(-2 pi j D  (D )   n), n in LatD1 }         *)
  721. (*                                     2   1                     *)
  722. (*                                         *)
  723. (*  The points in the lattice associated with D1 (LatD1) are none other      *)
  724. (*  than the set of fundamental cosets.                         *)
  725.  
  726. Options[ CommutableResamplingMatricesQ ] := { Dialogue -> False }
  727.  
  728. CommutableResamplingMatricesQ[ D1_, D2_, op___ ] :=
  729.     Block [    {anglesofA, anglesofB, cosets, dialogue, D1tinv, vectors},
  730.  
  731.         (*  check arguments  *)
  732.         If [ ! ( ResamplingMatrix[D1] && ResamplingMatrix[D2] ),
  733.              Return[Message[CommutableResamplingMatricesQ::invalid]] ];
  734.  
  735.         (*  check if dialogue is enabled  *)
  736.         dialogue = Replace[ Dialogue,
  737.                     toList[op] ~Join~
  738.                     Options[CommutableResamplingMatricesQ] ];
  739.  
  740.         (*  Condition #1  *)
  741.         If [ ! SameQ[D1 . D2, D2 . D1], Return[False] ];
  742.  
  743.         (*  Condition #2  *)
  744.         cosets = DistinctCosetVectors[D1];
  745.         D1tinv = Inverse[ Transpose[D1] ];
  746.         vectors = D1tinv . Transpose[cosets];
  747.         anglesofA = Union[Transpose[mymod[vectors]]];
  748.         anglesofB = Union[Transpose[mymod[Transpose[D2] . vectors]]];
  749.  
  750.         If [ dialogue,
  751.              Print["angles associated with the downsampling matrix:"];
  752.              Print[2 Pi anglesofA];
  753.              Print["angles associated with both resampling matrices:"];
  754.              Print[2 Pi anglesofB] ];
  755.  
  756.         SameQ[anglesofA, anglesofB] ]
  757.  
  758. (*  DiagonalMatrixQ  *)
  759. DiagonalMatrixQ[mat_] := False  /; ! MatrixQ[mat]
  760. DiagonalMatrixQ[mat_] :=
  761.         Block [ {cond = False, diag, dims, i},
  762.         dims = Dimensions[mat];
  763.         If [ dims[[1]] == dims[[2]],
  764.                      diag = Table[ mat[[i,i]], { i, 1, dims[[1]] } ];
  765.              cond = SameQ[mat, DiagonalMatrix[diag]] ];
  766.         cond ]
  767.  
  768. (*  DistinctCosetVectors--  resmat is a dim x dim matrix  *)
  769. DistinctCosetVectors[ resmat_?ResamplingMatrix ] :=
  770.     Apply[DistinctCosetVectors, Prepend[SmithNormalForm[resmat], resmat]]
  771.  
  772. DistinctCosetVectors[ resmat_, uinv_, lambda_, vinv_ ] :=
  773.     Block [    {absdet, cosets, curvertex, diagvector, dim,
  774.          invresmat, lowervertex, u, uppervertex, zerovector},
  775.  
  776.         absdet = Abs[Det[resmat]];
  777.         If [ SameQ[absdet, 0],
  778.              Message[ DistinctCosetVectors::zerodet ];
  779.              Return[] ];
  780.  
  781.         dim = Apply[Min, Dimensions[resmat]];
  782.         zerovector = Apply[Table, {0, {dim}}];
  783.         cosets = { zerovector };
  784.  
  785.         If [ SameQ[absdet, 1], Return[cosets] ];
  786.  
  787.         invresmat = Inverse[resmat];
  788.         u = Inverse[uinv];
  789.  
  790.         diagvector = Table[ lambda[[i,i]], {i, 1, dim} ];
  791.         uppervertex = Abs[ diagvector ];
  792.         lowervertex = curvertex = zerovector;
  793.  
  794.         (*  enumerate all points in the rectangular prism      *)
  795.         (*    delimited by the column vectors of the diagonal     *)
  796.         (*    matrix lambda, map them ny U^-1, and then modulo    *)
  797.         (*    each one with the original resampling matrix resmat *)
  798.         cosets = Table[ curvertex = IncList[ curvertex,
  799.                              lowervertex,
  800.                              uppervertex ];
  801.                 ResamplingMatrixMod[ u . curvertex,
  802.                                resmat, invresmat ],
  803.                 {absdet - 1} ];
  804.  
  805.         Prepend[cosets, zerovector] ]
  806.  
  807. (*  EuclidFactors--  p a + q b == k  with p, q, and k given w/ GCD(p,q) == 1 *)
  808. (*  1.  integer case  *)
  809. EuclidFactors[0, p_Integer, q_Integer] := {0, 0}
  810. EuclidFactors[k_Integer, p_Integer, q_Integer] :=
  811.     If [ p < q,
  812.          Reverse[ integerEuclidFactors[k, q, p] ],
  813.          integerEuclidFactors[k, p, q] ]
  814.  
  815. integerEuclidFactors[k_, p_, q_] :=
  816.     Block [    {a, b, continue, kmod, nump, numq, periods, pxq},
  817.         pxq = p q;
  818.         periods = intdivide[k, pxq];
  819.         kmod = Mod[k, pxq];
  820.         nump = intdivide[periods, 2];
  821.         numq = nump + Mod[periods, 2];
  822.         continue = True;
  823.         a = 0;
  824.         While [ continue,
  825.             b = ( kmod - p a ) / q;
  826.             If [ IntegerQ[b], continue = False, a++ ] ];
  827.         {a + q nump, b + p numq} ] /;
  828.     GCD[p, q] == 1
  829.  
  830. integerEuclidFactors[k_, p_, q_] :=
  831.     Message[EuclidFactors::notrelprime, p, q, "integers"]
  832.  
  833. (*  2. Integer matrix case  *)
  834. EuclidFactors[k_?IntegerVectorQ, p_?ResamplingMatrix, q_?ResamplingMatrix] :=
  835.     Block [    {detp, detq},
  836.         detp = Det[p];
  837.         detq = Det[q];
  838.         If [ Abs[detp] > Abs[detq],
  839.              Reverse[ vectorEuclidFactors[k, q, p, detq, detp] ],
  840.              vectorEuclidFactors[k, p, q, detp, detq] ] ]
  841.  
  842. vectorEuclidFactors[k_, p_, q_, detp_, detq_] :=
  843.     Block [    {a, b, cosets, i, numcosets, pinv, pinvDOTk, pinvDOTq},
  844.  
  845.         (* We can rewrite  k = p . a  +  q . b   as               *)
  846.         (*             -1             -1                          *)
  847.         (*            p   . k  -  p  . q . b  =  a             *)
  848.         pinv = Inverse[p];
  849.         pinvDOTk = pinv . k;
  850.         pinvDOTq = pinv . q;
  851.  
  852.         (* Now, we want to enumerate all possible values of b     *)
  853.         (* and find the a that makes the above equation give      *)
  854.         (* an integer vector.  We have mapped k into the          *)
  855.         (* fundamental paralellopiped so all we have to do is     *)
  856.         (* enumerate the cosets of q.                             *)
  857.         cosets = DistinctCosetVectors[q];
  858.         numcosets = Length[cosets];
  859.           For [ i = 1, i <= numcosets, i++,
  860.                 b = cosets[[i]];
  861.                 a = pinvDOTk - pinvDOTq . b;
  862.                 If [ IntegerVectorQ[a], Break[] ] ];
  863.         {a, b} ] /;
  864.     RelativelyPrime[p, q, Right]
  865.  
  866. vectorEuclidFactors[k_, p_, q_, detp_, detq_] :=
  867.     Message[ EuclidFactors::notrelprime, p, q,
  868.          "(on the right) integer matrices" ]
  869.  
  870.  
  871. (*  GCLD -- a and b are m x n; X is 2n x 2n; H and F are 2m x 2n     *)
  872. Options[GCLD] := { Dialogue -> False }
  873. GCLD[ a_, b_, op___ ] :=
  874.     Block [ {allflag, dialflag, dialogue, F, H, H11, H12, H21, H22,
  875.          m, n, oplist, X, zeromxn, zerovector},
  876.  
  877.         oplist = toList[op] ~Join~ Options[GCRD];
  878.         dialogue = Replace[Dialogue, oplist];
  879.         allflag = SameQ[dialogue, All];
  880.         dialflag = dialogue || allflag;
  881.         If [ dialflag, LCRMandGCLDintroduction[] ];
  882.  
  883.         {m, n} = Dimensions[a];
  884.         zerovector = Table[0, {n}];
  885.         zeromxn = Table[zerovector, {m}];
  886.         F = matrix4to1[a, b, zeromxn, zeromxn];
  887.  
  888.         {X, H} = RowHermiteForm[F, oplist];
  889.         {{H11, H12}, {H21, H22}} = matrix1to4[H, {m, n}];
  890.  
  891.         If [ allflag,
  892.              Print[ "Hermite Form (right side):" ];
  893.              Print[ MatrixForm[H] ];
  894.              If [ ! SameQ[F . X, H],
  895.               Print[ "Error:  F . X != H" ] ] ];
  896.  
  897.         H11 ] /;
  898.     Dimensions[a] == Dimensions[b] && ResamplingMatrix[a] &&
  899.       ResamplingMatrix[b]
  900.  
  901. (* GCRD -- a and b are m x n, X is 2m x 2m, and H is 2m x 2n *)
  902. Options[GCRD] := { Dialogue -> False }
  903. GCRD[ a_, b_, op___ ] :=
  904.     Block [ {allflag, dialflag, dialogue, F, H, H11, H12, H21, H22,
  905.          m, n, oplist, X, zeromxn, zerovector},
  906.  
  907.         oplist = toList[op] ~Join~ Options[GCRD];
  908.         dialogue = Replace[Dialogue, oplist];
  909.         allflag = SameQ[dialogue, All];
  910.         dialflag = dialogue || allflag;
  911.         If [ dialflag, LCLMandGCRDintroduction[] ];
  912.  
  913.         {m, n} = Dimensions[a];
  914.         zerovector = Table[0, {n}];
  915.         zeromxn = Table[zerovector, {m}];
  916.         F = matrix4to1[a, zeromxn, b, zeromxn];
  917.  
  918.         {X, H} = ColumnHermiteForm[F, oplist];
  919.         {{H11, H12}, {H21, H22}} = matrix1to4[H, {m, n}];
  920.  
  921.         If [ allflag,
  922.              Print[ "Hermite Form (right side):" ];
  923.              Print[ MatrixForm[H] ];
  924.              If [ ! SameQ[X . F, H],
  925.               Print[ "Error:  X . F != H" ] ] ];
  926.  
  927.         H11 ] /;
  928.     Dimensions[a] == Dimensions[b] && ResamplingMatrix[a] &&
  929.       ResamplingMatrix[b]
  930.  
  931. (*  IncList  *)
  932. IncList[list_, start_, end_] :=
  933.     Block [    {incflag, newlist},
  934.         newdigit[d_, s_, e_] :=
  935.             Which [ incflag && SameQ[d + 1, e],
  936.                   s,
  937.                 incflag,
  938.                   incflag = False;
  939.                   d + 1,
  940.                 True,
  941.                   d ];
  942.         incflag = True;
  943.         newlist = newdigit[list, start, end];
  944.         If [ SameQ[newlist, start], end, newlist ] ]
  945.  
  946. SetAttributes[newdigit, Listable]
  947.  
  948. (*  ImpulseTrain  *)
  949. ImpulseTrain[D_Integer, n_Integer] := 1        /; IntegerQ[n / D]
  950. ImpulseTrain[D_Integer, n_Integer] := 0
  951.  
  952. ImpulseTrain[D_?ResamplingMatrix, n_?IntegerVectorQ] := 1    /;
  953.     IntegerVectorQ[ Inverse[D] . n ]
  954. ImpulseTrain[D_?ResamplingMatrix, n_?IntegerVectorQ] := 0
  955.  
  956. ImpulseTrain[{D_}, {n_}] := ImpulseTrain[D, n]
  957. ImpulseTrain[D_List, n_List] :=
  958.     Apply[Times, Map[ itrain, Transpose[ { D, n } ] ] ] /;
  959.     VectorQ[D] && VectorQ[n] && Length[D] == Length[n]
  960.  
  961. itrain[{D_, n_}] := ImpulseTrain[D, n]
  962.  
  963. (*  IntegerVectorQ  *)
  964. IntegerVectorQ[arg_] := VectorQ[arg] && Apply[And, Map[IntegerQ, arg]]
  965.  
  966. (*  LCLM -- a and b are m x n, X is 2m x 2m, and H is 2m x 2n  *)
  967. Options[LCLM] := { Dialogue -> False }
  968. LCLM[ a_, b_, op___ ] :=
  969.     Block [ {allflag, dialflag, dialogue, F, H, m, n, oplist, X, 
  970.          X11, X12, X21, X22, zeromxn, zerovector},
  971.  
  972.         oplist = toList[op] ~Join~ Options[LCLM];
  973.         dialogue = Replace[Dialogue, oplist];
  974.         allflag = SameQ[dialogue, All];
  975.         dialflag = dialogue || allflag;
  976.         If [ dialflag, LCLMandGCRDintroduction[] ];
  977.  
  978.         {m, n} = Dimensions[a];
  979.         zerovector = Table[0, {n}];
  980.         zeromxn = Table[zerovector, {m}];
  981.         F = matrix4to1[a, zeromxn, b, zeromxn];
  982.  
  983.         {X, H} = ColumnHermiteForm[F, oplist];
  984.         {{X11, X12}, {X21, X22}} = matrix1to4[X, {m, m}];
  985.  
  986.         If [ allflag,
  987.              Print[ "Hermite Form (right side):" ];
  988.              Print[ MatrixForm[H] ];
  989.              If [ ! SameQ[X . F, H],
  990.               Print[ "Error:  X . F != H" ] ] ];
  991.  
  992.         X21 . a ] /;
  993.     Dimensions[a] == Dimensions[b] && ResamplingMatrix[a] &&
  994.       ResamplingMatrix[b]
  995.  
  996. (*  LCRM -- a and b are m x n; X is 2n x 2n; H and F are 2m x 2n     *)
  997. Options[LCRM] := { Dialogue -> False }
  998. LCRM[ a_, b_, op___ ] :=
  999.     Block [ {allflag, dialflag, dialogue, F, H, m, n, oplist, X,
  1000.          X11, X12, X21, X22, zeromxn, zerovector},
  1001.  
  1002.         oplist = toList[op] ~Join~ Options[GCRD];
  1003.         dialogue = Replace[Dialogue, oplist];
  1004.         allflag = SameQ[dialogue, All];
  1005.         dialflag = dialogue || allflag;
  1006.         If [ dialflag, LCRMandGCLDintroduction[] ];
  1007.  
  1008.         {m, n} = Dimensions[a];
  1009.         zerovector = Table[0, {n}];
  1010.         zeromxn = Table[zerovector, {m}];
  1011.         F = matrix4to1[a, b, zeromxn, zeromxn];
  1012.  
  1013.         {X, H} = RowHermiteForm[F, oplist];
  1014.         {{X11, X12}, {X21, X22}} = matrix1to4[X, {n, n}];
  1015.  
  1016.         If [ allflag,
  1017.              Print[ "Hermite Form (right side):" ];
  1018.              Print[ MatrixForm[H] ];
  1019.              If [ ! SameQ[F . X, H],
  1020.               Print[ "Error:  F . X != H" ] ] ];
  1021.  
  1022.         a . X12 ] /;
  1023.     Dimensions[a] == Dimensions[b] && ResamplingMatrix[a] &&
  1024.       ResamplingMatrix[b]
  1025.  
  1026. (*  NormalizeSamplingMatrix  *)
  1027. NormalizeSamplingMatrix[ sampmat_ ] :=
  1028.     Block [    {diag},
  1029.         diag = Map[ vectorgcd, sampmat ];
  1030.         { DiagonalMatrix[diag], mat / diag } ]
  1031.  
  1032. (*  RegUnimodularResMatrixQ  *)
  1033. RegUnimodularResMatrixQ[mat_] := ResamplingMatrix[mat] && Abs[Det[mat]] == 1
  1034.  
  1035. (*  ReorderResampling  *)
  1036. ReorderResampling[ m_, nvars_, nlist_ ] :=
  1037.     ReorderResampling[ m, nvars, nlist, nvars ]
  1038. ReorderResampling[ m_, nvars_, nvars_, zlist_ ] :=
  1039.     {m, nvars, zlist}
  1040. ReorderResampling[ m_?Matrix, nvars_List, nlist_List, zlist_List ] :=
  1041.     Block [    {i, j, locs, mnew, nnew, t, znew},
  1042.         locs = Flatten[ Map[Position[nlist, #1]&, nvars] ];
  1043.         znew = Part[zlist, locs];
  1044.         dim = Length[nvars];
  1045.         mnew = m;
  1046.         nnew = nvars;
  1047.         For [ i = 1, i <= dim, i++,
  1048.           For [ j = i+1, j <= dim, j++,
  1049.             If [ locs[[i]] > locs[[j]],
  1050.              t = locs[[i]]; locs[[i]] = locs[[j]]; locs[[j]] = t;
  1051.              t = znew[[i]]; znew[[i]] = znew[[j]]; znew[[j]] = t;
  1052.              t = nnew[[i]]; nnew[[i]] = nnew[[j]]; nnew[[j]] = t;
  1053.              mnew = permuteMatrix[mnew, i, j] ] ] ];
  1054.         {mnew, nnew, znew} ] /;
  1055.     SameQ[ Dimensions[m],
  1056.            { Length[nlist], Length[nlist] },
  1057.            { Length[zlist], Length[zlist] } ]
  1058.  
  1059. permuteMatrix[m_, i_, j_] :=
  1060.     Block [    {mnew = m, t},
  1061.         (* switch rows *)
  1062.         t = mnew[[i]]; mnew[[i]] = mnew[[j]]; mnew[[j]] = t;
  1063.  
  1064.         (* switch i,j entries in each row *)
  1065.         t = mnew[[i,j]]; mnew[[i,j]] = mnew[[i,i]]; mnew[[i,i]] = t;
  1066.         t = mnew[[j,j]]; mnew[[j,j]] = mnew[[j,i]]; mnew[[j,i]] = t;
  1067.  
  1068.         mnew ]
  1069.  
  1070. (*  RelativelyPrime  *)
  1071. RelativelyPrime[a_Integer, b_Integer, rest___] :=
  1072.     ( GCD[a, b] == 1 )
  1073. RelativelyPrime[a_?PolynomialQ, b_?PolynomialQ, rest___] :=
  1074.     ( PolynomialGCD[a, b] == 1 )
  1075. RelativelyPrime[a_?DiagonalMatrixQ, b_?DiagonalMatrixQ, rest___] :=
  1076.     Block [    {i, n, prime = True},
  1077.         n = First[ Dimensions[a] ];
  1078.         For [ i = 1, i <= n, i++,
  1079.               If [ GCD[ a[[i,i]], b[[i,i]] ] != 1,
  1080.                prime = False; Break[] ] ];
  1081.         prime ] /;
  1082.     ResamplingMatrix[a] && ResamplingMatrix[b] &&
  1083.       Dimensions[a] == Dimensions[b]
  1084. RelativelyPrime[a_?ResamplingMatrix, b_?ResamplingMatrix, Left] :=
  1085.     RegUnimodularResMatrixQ[ GCRD[a, b] ] /;  (* or does gcrd = Imat? *)
  1086.     Dimensions[a] == Dimensions[b]
  1087. RelativelyPrime[a_?ResamplingMatrix, b_?ResamplingMatrix, Right] :=
  1088.     RegUnimodularResMatrixQ[ GCLD[a, b] ] /;  (* or does gcld = Imat? *)
  1089.     Dimensions[a] == Dimensions[b]
  1090.  
  1091. (*  ResamplingMatrix  *)
  1092. intcond[a_Integer] := True
  1093. intcond[a_?NumberQ] := False
  1094.  
  1095. intcond/: Format[ intcond[a_] ] := StringForm[ "Head[``] == Integer", a ]
  1096.  
  1097. ResamplingMatrix[a_] :=
  1098.     MatrixQ[ a ] && Apply[ SameQ, Dimensions[a] ] &&
  1099.     Apply[ And, Map[intcond, Flatten[a]] ] && ( Det[a] != 0 )
  1100.  
  1101. (*  ResamplingMatrixMod  *)
  1102. ResamplingMatrixMod[n_?IntegerVectorQ, sampmat_?MatrixQ] :=
  1103.     ResamplingMatrixMod[n, sampmat, Inverse[sampmat] ] /;
  1104.     ( ! SameQ[ ResamplingMatrix[sampmat], False ] ) &&
  1105.     Apply[ SameQ, Prepend[Dimensions[sampmat], Length[n]] ]
  1106.  
  1107. ResamplingMatrixMod[n_, sampmat_, invsampmat_] :=
  1108.     n - ( sampmat . Floor[ invsampmat . n ] )
  1109.  
  1110. (*  RowHermiteForm  *)
  1111. Options[RowHermiteForm] := { Dialogue -> False }
  1112. RowHermiteForm[ mat_, op___ ] :=
  1113.     Block [    {Ht, oplist, Xt},
  1114.         oplist = toList[op] ~Join~ Options[RowHermiteForm];
  1115.         {Xt, Ht} = ColumnHermiteForm[ Transpose[mat], oplist ];
  1116.         { Transpose[Xt], Transpose[Ht] } ] /;
  1117.     MatrixQ[mat] && IntegerVectorQ[ Flatten[mat] ]
  1118.  
  1119. (*  UpsampledSystem  *)
  1120. linearmap = {}
  1121.  
  1122. addvector[ a_ ] := linearmap = a /; EmptyQ[linearmap]
  1123. addvector[ a_ ] := a /; MemberQ[linearmap, a]        
  1124. addvector[ a_ ] := Block [ {}, AppendTo[linearmap, a]; a ]
  1125.  
  1126. getcoeffs[ expr_, vlist_ ] :=
  1127.     Block [ {coeffs},
  1128.         coeffs = Map[ Coefficient[expr, #]&, vlist ];
  1129.         {expr - coeffs . vlist, coeffs} ]
  1130.  
  1131. testcoeffs[ {leftover_, coeffs_}, vlist_, zerovector_ ] :=
  1132.     ! SameQ[coeffs, zerovector] && MyFreeQ[ {leftover, coeffs}, vlist ] 
  1133.  
  1134. mDlinearMapping[ expr_, upvarlist_, coeffun_ ] :=
  1135.     Block [ {dims, uppair, zerovector},
  1136.         dims = Length[upvarlist];
  1137.         zerovector = Table[0, {dims}];
  1138.         linearmap = {};
  1139.         Collect[expr, upvarlist] /.
  1140.         ( a_ :> addvector[ coeffun[a, upvarlist][[2]] ] /;
  1141.           testcoeffs[coeffun[a, upvarlist], upvarlist, zerovector] );
  1142.         linearmap ]
  1143.  
  1144. notcoupled[upmatrix_, rowcol_, dims_] :=
  1145.     Block [    {oneElementVector},
  1146.         oneElementVector = Table[0, {dims}];
  1147.         oneElementVector[[rowcol]] = _;
  1148.         MatchQ[upmatrix[[rowcol]], oneElementVector] &&
  1149.           MatchQ[Transpose[upmatrix][[rowcol]], oneElementVector] ]
  1150.  
  1151. UpsampledSystem[ x_, upvarlist_, coeffun_:getcoeffs ] :=
  1152.     Block [    {dims, coupledvars, i, j, maxi, pos, rowcol, temp, upmatrix},
  1153.         upmatrix = mDlinearMapping[x, upvarlist, coeffun];
  1154.         maxi = dims = Length[upvarlist];
  1155.         coupledvars = upvarlist;
  1156.         rowcol = 1;
  1157.         For [ i = 1, i <= maxi, i++,
  1158.               If [ notcoupled[upmatrix, rowcol, dims],
  1159.                --dims;
  1160.                pos = {rowcol, rowcol};
  1161.                    upmatrix = Drop[upmatrix, pos];
  1162.                upmatrix = Transpose[Drop[Transpose[upmatrix], pos]];
  1163.                coupledvars = Drop[coupledvars, pos],
  1164.                rowcol++ ] ];
  1165.         For [ i = 1, i <= dims, i++,
  1166.               If [ SameQ[upmatrix[[i,i]], 0],
  1167.                For [ j = 1, j <= dims, j++,
  1168.                  If [ ! SameQ[i,j] &&
  1169.                       ! SameQ[upmatrix[[j,i]], 0],
  1170.                       temp = upmatrix[[i]];
  1171.                       upmatrix[[i]] = upmatrix[[j]];
  1172.                       upmatrix[[j]] = temp ] ] ] ];
  1173.         {upmatrix, coupledvars} ]
  1174.  
  1175.  
  1176. (*  S M I T H     D E C O M P O S I T I O N     R O U T I N E S  *)
  1177.  
  1178. (* SmithNormalForm *)
  1179. Options[ SmithNormalForm ] := { Dialogue -> False }
  1180.  
  1181. SmithNormalForm[ mat_?MatrixQ, op___ ] :=
  1182.     Block [    {curmat, dialogue, dim, endflag, identm, identn,
  1183.          m, n, oplist, r, submatleft, submatright, transmatleft,
  1184.          transmatright, uinv, vinv },
  1185.  
  1186.         oplist = toList[op] ~Join~ Options[SmithNormalForm];
  1187.         dialogue = Replace[Dialogue, oplist];
  1188.  
  1189.         curmat = mat;
  1190.         {m, n} = Dimensions[mat];
  1191.         uinv = identm = IdentityMatrix[m];
  1192.         vinv = identn = IdentityMatrix[n];
  1193.         r = Min[m, n];
  1194.             
  1195.         For [ dim = 0, dim < r - 1, dim++,
  1196.  
  1197.               If [ dialogue || SameQ[dialogue, All],
  1198.                Print[]; Print[ "Iteration #", dim + 1 ]; Print[] ];
  1199.  
  1200.               endflag = False;
  1201.               While [ ! endflag,
  1202.  
  1203.                   (* part a: move min element to (1,1) *)
  1204.  
  1205.                   { minvalue, transmatleft, transmatright } =
  1206.                 SmithFormA[curmat, dim, m, n, identm, identn];
  1207.                   curmat = transmatleft . curmat . transmatright;
  1208.  
  1209.                   SmithFormTrace[dialogue, "Part A", curmat];
  1210.  
  1211.                   (* part b: eliminate elements on first row/col *)
  1212.  
  1213.                   { submatleft, submatright } =
  1214.                 SmithFormB[ curmat, dim, minvalue,
  1215.                         m, n, identm, identn ];
  1216.                   curmat = submatleft . curmat . submatright;
  1217.  
  1218.                   SmithFormTrace[dialogue, "Part B"];
  1219.                   SmithFormTrace2[dialogue, "left", submatleft];
  1220.                   SmithFormTrace2[dialogue, "right", submatright];
  1221.                   SmithFormTrace[dialogue, " ", curmat];
  1222.  
  1223.                   (* update U^-1 and V^-1 *)
  1224.                   uinv = submatleft . transmatleft . uinv;
  1225.                   vinv = vinv . transmatright . submatright;
  1226.  
  1227.                   (* check for the ending condition *)
  1228.                   endflag = SmithFormEndTest[curmat, dim, m, n] ] ];
  1229.  
  1230.     {uinv, curmat, vinv} ]            
  1231.  
  1232. (* SmithOrderedForm --  sort the diagonal matrix *)
  1233. Options[SmithOrderedForm] := Options[SmithNormalForm]
  1234.  
  1235. SmithOrderedForm[ mat_List, op___ ] :=
  1236.     Block [    {d, dim, i, identm, identn, j, jend, m, n, r,
  1237.          temp, utransmat, vtransmat, uinv, vinv},
  1238.  
  1239.         {m, n} = Dimensions[mat];
  1240.  
  1241.         (* First, find its Smith Normal Form decomposition *)
  1242.         { uinv, d, vinv } = SmithNormalForm[ mat, op ];
  1243.  
  1244.         (* Factor out the signs of the diagonal elements *)
  1245.         (*   the diagonal values will all be positive    *)
  1246.         (*   propagate the sign info to U^-1         *)
  1247.         Clear[i];
  1248.         uinv = Sign[ Table[ Take[d[[i]], m], {i, 1, m} ] ] . uinv;
  1249.         d = Abs[d];
  1250.  
  1251.         (* Sort the elements along the diagonal *)
  1252.         utransmat = identm = IdentityMatrix[m];
  1253.         vtransmat = identn = IdentityMatrix[n];
  1254.         r = Min[m, n];
  1255.         For [ i = 1, i <= r, i++,
  1256.               jend = r - i;
  1257.               For [ j = 1, j <= jend, j++,
  1258.                 If [ d[[j,j]] > d[[j+1,j+1]],
  1259.                  temp = d[[j,j]];
  1260.                        d[[j,j]] = d[[j+1,j+1]];
  1261.                        d[[j+1,j+1]] = temp;
  1262.  
  1263.                        temp = utransmat[[j]];
  1264.                        utransmat[[j]] = utransmat[[j+1]];
  1265.                        utransmat[[j+1]] = temp;
  1266.  
  1267.                        temp = vtransmat[[j]];
  1268.                        vtransmat[[j]] = vtransmat[[j+1]];
  1269.                        vtransmat[[j+1]] = temp ] ] ];
  1270.  
  1271.         (* We switched rows instead of columns, so transpose it *)
  1272.         vtransmat = Transpose[vtransmat];
  1273.  
  1274.         (* We have changed  U D V  to  U Tu^-1 Tu D Tv Tv^-1 V    *)
  1275.         (*   Dnew = Tu D Tv  which was done in the sort above    *)
  1276.         (*   Unew = U Tu^-1  ==>  Unew^-1 = Tu U^-1        *)
  1277.         (*   Vnew = Tv^-1 V  ==>  Vnew^-1 = V^-1 Tv        *)
  1278.         uinv = utransmat . uinv;
  1279.         vinv = vinv . vtransmat;
  1280.  
  1281.         (* return U^-1, D, and V^-1 as a list *)
  1282.         { uinv, d, vinv } ]
  1283.  
  1284. (* SmithReducedForm *)
  1285. (* -- A is the original matrix and A = U D V  *)
  1286. (* -- D has only positive values              *)
  1287. (* -- start with        U^-1 A V^-1   =   D   *)
  1288. (* -- iterate with    G U^-1 A V^-1 H = G D H *)
  1289. Options[SmithReducedForm] := Options[SmithNormalForm]
  1290.  
  1291. SmithReducedForm[ mat_List, op___ ] :=
  1292.     Block [    {adj, d, di, gcd, g, h, i, identm, identn,
  1293.          lambda, lastd, lasti, lcm, m, mu, n, r, uinv, vinv},
  1294.  
  1295.         (*  Find the dimensions of the matrix *)
  1296.         { m, n } = Dimensions[mat];
  1297.         r = Min[m, n];
  1298.         identm = IdentityMatrix[m];
  1299.         identn = IdentityMatrix[n];
  1300.  
  1301.         (*  Put the matrix in Smith Ordered Form  *)
  1302.         { uinv, d, vinv } = SmithOrderedForm[mat, op];
  1303.  
  1304.         (*  Convert the diagonal  *)
  1305.         lastd = d[[1,1]];
  1306.         lasti = 1;
  1307.         For [ i = 2, i <= r, i++,
  1308.               di = d[[i,i]];
  1309.               (* gcd = GCD[lastd, di];
  1310.                  {lambda, mu} = bezoutwithgcd[lastd, di, gcd]; *)
  1311.               {gcd, {lambda, mu}} = ExtendedGCD[lastd, di];
  1312.               If [ gcd == lastd, lastd = di; Continue[] ];
  1313.  
  1314.               g = identm;
  1315.               h = identn;
  1316.               lcm = LCM[lastd, di];
  1317.  
  1318.               (* define g matrix *)
  1319.               g[[lasti, lasti]] = 1;
  1320.               g[[lasti, i]] = 1;
  1321.               g[[i, lasti]] = -mu di / gcd;
  1322.               g[[i, i]] = lambda lastd / gcd;
  1323.  
  1324.               (* define h matrix *)
  1325.               h[[lasti, lasti]] = lambda;
  1326.               h[[lasti, i]] = -di / gcd;
  1327.               h[[i, lasti]] = mu;
  1328.               h[[i, i]] = lastd / gcd;
  1329.  
  1330.               (* adjust diagonal elements *)
  1331.               d[[lasti, lasti]] = gcd;
  1332.               d[[i,i]] = lastd = lcm;
  1333.  
  1334.               (* update uinv and vinv *)
  1335.               uinv = g . uinv;
  1336.               vinv = vinv . h;
  1337.  
  1338.               lasti = i ];
  1339.  
  1340.         { uinv, d, vinv } ]
  1341.  
  1342. (* SmithFormSameV *)
  1343. SmithFormSameV[ smithfun_, m1_, m2_, op___] :=
  1344.     Block [    {d1, d2, diag, u1inv, u2, u2d2, u2inv, v1inv, v2inv},
  1345.  
  1346.         {u1inv, d1, v1inv} = smithfun[ m1, op ];
  1347.  
  1348.         v2inv = v1inv;
  1349.         u2d2 = m2 . v2inv;            (* U2 D2 = M2 V2^-1 *)
  1350.         diag = Map[vectorgcd, Transpose[u2d2]];    (* D2 is the gcd of *)
  1351.         d2 = DiagonalMatrix[diag];        (*   cols of U2 D2  *)
  1352.         u2 = u2d2 . DiagonalMatrix[1/diag];    (* U2 = U2 D2 D2^-1 *)
  1353.         u2inv = Inverse[u2];
  1354.  
  1355.         {Abs[Det[u2inv]] == 1, {u1inv, d1, v1inv}, {u2inv, d2, v2inv}} ]
  1356.  
  1357. (* SmithFormSameU *)
  1358. SmithFormSameU[ smithfun_, m1_, m2_, op___ ] :=
  1359.     Block [    {d1, d2, diag, u1inv, u2inv, u2invd2, v1inv, v2inv},
  1360.  
  1361.         {u1inv, d1, v1inv} = smithfun[ m1, op ];
  1362.  
  1363.         u2inv = u1inv;
  1364.         d2v2 = u2inv . m2;            (* D2 V2 = U2^-1 M2 *)
  1365.         diag = Map[vectorgcd, d2v2];        (* D2 is the gcd of *)
  1366.         d2 = DiagonalMatrix[diag];        (*   rows of D2 V2  *)
  1367.         v2 = DiagonalMatrix[1/diag] . d2v2;    (* V2 = D2^-1 D2 V2 *)
  1368.         v2inv = Inverse[v2];
  1369.  
  1370.         {Abs[Det[v2inv]] == 1, {u1inv, d1, v1inv}, {u2inv, d2, v2inv}} ]
  1371.  
  1372.  
  1373. (*  E N D     P A C K A G E  *)
  1374.  
  1375. End[]
  1376. EndPackage[]
  1377.  
  1378. If [ TrueQ[ $VersionNumber >= 2.0 ],
  1379.      On[ General::spell ];
  1380.      On[ General::spell1 ] ]
  1381.  
  1382.  
  1383. (*  H E L P     I N F O R M A T I O N  *)
  1384.  
  1385. Block [    {newfuns},
  1386.     newfuns = 
  1387.       { BezoutNumbers,        CommutableResamplingMatricesQ,
  1388.         DiagonalMatrixQ,        DistinctCosetVectors,
  1389.         EuclidFactors,
  1390.         GCLD,            GCRD,
  1391.         ColumnHermiteForm,        ImpulseTrain,
  1392.         IncList,            IntegerVectorQ,
  1393.         LCLM,            LCRM,
  1394.         NormalizeSamplingMatrix,    RegUnimodularResMatrixQ,
  1395.         RelativelyPrime,        ReorderResampling,
  1396.         ResamplingMatrix,        ResamplingMatrixMod,
  1397.         RowHermiteForm,
  1398.         SmithFormSameU,        SmithFormSameV,
  1399.         SmithNormalForm,        SmithOrderedForm,
  1400.         SmithReducedForm,        UpsampledSystem };
  1401.     Combine[ SPfunctions, newfuns ];
  1402.     Apply[ Protect, newfuns ] ]
  1403.  
  1404.  
  1405. (*  E N D     M E S S A G E  *)
  1406.  
  1407. Print["Functions supporting multirate signal manipulations have been loaded."]
  1408. Null
  1409.  
  1410.